#pragma once

#include <iostream>
#include <string>
#include <cstdlib>
#include <sys/socket.h>
#include <unistd.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <strings.h>
#include "Log.hpp"
#include "Common.hpp"
#include "InetAddr.hpp"

using namespace Lock_Module;
namespace Sock_M
{
    // 采用模版方法设计模式
    // 基类（socket）--派生类(TcpSock,UdpSock)继承进行重写
    using namespace Lock_Module;
    using namespace Log_Module;
    using namespace std;
    class Socket;
    using SockPtr = std::shared_ptr<Socket>;

    class Socket
    {
    public:
        virtual ~Socket() = default;
        virtual void Socket_Create() = 0;
        virtual void Socket_option() = 0;
        virtual bool Bind(int port) = 0;
        virtual bool Listen() = 0;
        virtual void close() = 0;
        virtual int Recv(string *out) = 0;
        virtual int Send(const string &in) = 0;
        virtual int Fd() = 0;
        virtual int Accept(InetAddr *Client, int *out_errno) = 0;

        void BulidTcpSock(int port)
        {
            Socket_Create();
            Socket_option();
            Bind(port);
            Listen();
        }
    };

    class Tcp_Socket : public Socket
    {
    private:
        int _sockfd;

    public:
        Tcp_Socket() : _sockfd(gdefaultsockfd)
        {
        }
        Tcp_Socket(int sockfd) : _sockfd(sockfd)
        {
        }
        virtual ~Tcp_Socket()
        {
        }
        virtual void Socket_Create() override
        {
            _sockfd = socket(AF_INET, SOCK_STREAM, 0);
            if (_sockfd < 0)
            {
                LOG(Log_Level::ERROR) << "create socket error";
                exit(SCOKET_ERR);
            }
            SetNoBlack(_sockfd);
            LOG(Log_Level::INFO) << "create socket success fd:" << _sockfd;
        }
        virtual void Socket_option() override
        {
            // 在http进行四次挥手的时候，有time_wait状态，需要设置SO_REUSEADDR
            int opt = 1;
            int n = setsockopt(_sockfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
            (void)n;
        }
        virtual bool Bind(int port) override
        {
            if (_sockfd == gdefaultsockfd)
                return false;
            InetAddr addr(port);
            int n = bind(_sockfd, addr.Netaddr(), addr.NetAddrLen());
            if (n < 0)
            {
                LOG(Log_Level::ERROR) << "bind socket error";
                exit(BIND_ERR);
            }
            LOG(Log_Level::INFO) << "bind socket success";
            return true;
        }
        virtual bool Listen() override
        {
            if (_sockfd == gdefaultsockfd)
                return false;
            int n = listen(_sockfd, 8);
            if (n < 0)
            {
                LOG(Log_Level::ERROR) << "listen socket error";
                exit(Listen_ERR);
            }
            LOG(Log_Level::INFO) << "listen socket success";
            return true;
        }
        int Accept(InetAddr *Client, int *out_errno) override
        {
            if (!Client)
            {
                struct sockaddr_in peer;
                socklen_t len = sizeof(peer);
                int newfd = accept(_sockfd, Change(&peer), &len);
                *out_errno = errno;
                if (newfd < 0)
                {
                    LOG(Log_Level::WARNING) << "accept socket error";
                    return -1;
                }
                Client->setAddr(peer, len);
                return newfd;
            }
        }
        virtual void close() override
        {
            if (_sockfd != gdefaultsockfd)
                return;
            ::close(_sockfd);
        }
        virtual int Recv(string *out) override
        {
            // 是输出型参数
            char buf[1024];
            int size = recv(_sockfd, buf, sizeof(buf), 0);
            if (size > 0)
            {
                buf[size] = 0;
                *out = buf;
            }
            return size;
        }

        virtual int Send(const string &in) override
        {
            int size = send(_sockfd, in.c_str(), in.size(), 0);
            return size;
        }

        virtual int Fd() override
        {
            return _sockfd;
        }
    };
}